<!DOCTYPE html>
<html style="margin: 0 12px;">
  <head>
    <meta charset="utf-8">
    <title>Test JS-Eval</title>
    <script type="text/javascript">
"undefined" === typeof globalThis && (window.globalThis = window);
![].includes&&(function(){
var noArrayFind=![].find;Object.defineProperty(Array.prototype,"includes",{
enumerable:false,value:function includes(value,ind){
  return this.indexOf(value,ind)>=0;}});
Object.assign||(Object.assign=function(dest){for(var i=1,len=arguments.length;
  i<len;i++){var src=arguments[i];if(!src){continue;}
    for(var key in src){dest[key]=src[key];}}return dest;});
noArrayFind&&Object.defineProperties(Array.prototype,{find:{enumerable:false,
value:function find(cond){var ind=this.findIndex(cond);
  return ind>=0?this[ind]:void 0;}},
findIndex:{enumerable:false,value:function findIndex(cond){
    for(var i=0;i<this.length;i++){if(cond(this[i],i,this)){return i;}}
return-1;}}});if("".includes){return;}
var StringCls=String.prototype;
"".startsWith?Promise.resolve||(Promise.resolve=Promise.resolved):
Object.defineProperties(StringCls,{startsWith:{enumerable:false,value:
  function startsWith(s){return 0===this.lastIndexOf(s,0);}},
endsWith:{enumerable:false,value:function endsWith(s){
  var i=this.length-s.length;return i>=0&&this.indexOf(s,i)===i;}}});
Object.defineProperty(StringCls,"includes",{enumerable:false,value:
  function myIncludes(s,pos){return this.indexOf(s,pos)>=0;}});
var ver=navigator.userAgent.match(/\bChrom(?:e|ium)\/(\d+)/);
if(ver&&+ver[1]<38){
var proto_1={add:function(k){this.map_[k]=1;},
clear:function(){this.map_=Object.create(null);},
delete:function(k){var old=k in this.map_;delete this.map_[k];return old;},
forEach:function(cb){var isSet=this.isSet_,map=this.map_;
  for(var key in map){isSet?cb(key):cb(map[key],key);}},
get:function(k){return this.map_[k]},has:function(k){return k in this.map_},
set:function(k,v){this.map_[k]=v;}};
var setProto_1=Object.setPrototypeOf?function(opt){
  Object.setPrototypeOf(opt,proto_1);}:function(obj){obj.__proto__=proto_1};
globalThis.Set=function(){setProto_1(this);this.map_=Object.create(null);this.isSet_=1;};
globalThis.Map=function(){setProto_1(this);this.map_=Object.create(null);this.isSet_=0;};}
})();
typeof chrome !== "undefined" || (window.chrome = {});
(chrome.runtime || (chrome.runtime={})).getManifest = function(){return {content_security_policy:{extension_pages:""}}}
    </script>
  </head>
  <body>
    <div style="margin-left: 20px;">Result:</div>
    <div id="resultDiv" style="white-space: pre;"></div>
    <hr>
    <script type="text/javascript">
      var resultDiv = document.querySelector("#resultDiv")
      var VApi = { v: null, z: { v: typeof Reflect !== "undefined" ? 51 : 32 },
                   t: function (_msg) { /* empty */ }}
      var __filename = null
      var define = function (depNames, factory) {
        (factory || depNames)(null, window.simple_eval = {})
      };
    </script>
    <script type="module" src="chrome-extension://hfjbmagddngcpeloejdejnfgbamkjaeg/lib/simple_eval.js"></script>
    <script type="text/javascript" defer>
"use strict";
//#region prepare
window.onload = function () {
  if (VApi.v) {
    onLoadWrapper()
  } else {
    const el = document.createElement("script")
    el.src = "chrome-extension://hfjbmagddngcpeloejdejnfgbamkjaeg/lib/simple_eval.js"
    el.onload = onLoadWrapper
    document.body.appendChild(el)
  }
};
function onLoadWrapper () { setTimeout(onLoad, 100) }
var jsEval = function (code) {
  var error = null, value
  code = normalizeCode(code)
  try {
    value = VApi.v.vimiumEval(code)
  } catch (e) {
    error = e
  }
  return [code, error ? null : value, error]
}
var normalizeCode = function (code) {
  if (typeof code === "function") {
    code = (code + "")
    if (code.endsWith("}")) {
      code = code.slice(code.indexOf("{") + 1, -1)
    } else if (/^\(\s*\)\s*=>/.test(code)) {
      code = code.slice(code.indexOf("=>") + 2)
    }
  }
  return code.trim()
}
var formatCode = function (code) {
  code = code.trim().replace(/  +/g, " ")
  return '"' + (code.length > 30
    ? code.slice(0, 30).replace(/\n/g, "\\n") + '"\u2026' : code + '"') }
var formatAnyObject = function (value) { try {
  var keys = Object.getOwnPropertyNames(value).concat(
      Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(value) : []), str = "{ "
  for (var i = 0; i < keys.length; i++) {
    var key = typeof keys[i] === "symbol" ? "[" + String(keys[i]) + "]"
        : /[\w$]+/.test(keys[i]) ? keys[i] : JSON.stringify(keys[i])
    var desc = Object.getOwnPropertyDescriptor(value, keys[i])
    desc.enumerable || (str += "non-E ")
    if (desc.value) {
      const val2 = formatValue(desc.value)
      str += typeof desc.value === "function" && val2[0] !== "("
        && !val2.startsWith("function") ? val2 + ", "
        : key + ": " + val2 + ", "
    }
    else {
      desc.writable || (str += "non-W ")
      desc.get && (str += formatValue(desc.get) + ", ")
      desc.set && (str += formatValue(desc.set) + ", ")
    }
  }
  return str.length > 2 ? str.slice(0, -2) + " }" : "{}"
} catch (e) {} }
var formatFunc = function (value) { return (value + "")
  .replace(/;(?!\n)/g, ";\n").replace(/\/\/[^\n]*|\/\*[^]*?\*\//, "")
  .replace(/\n\s*\n/g, "\n").replace(/\n {4} +/g, "\n    ")
  .replace(/'([^'\\]|\\[^])*'/g, function(s){
    return JSON.stringify(JSON.parse(('"' + s.slice(1, -1) + '"')
        .replace(/\\(x..|\\)/g, function(_,hex){
      return hex.length > 1 ? "\\u00" + hex.slice(1) : "\\\\"
    })))
  })
}
var __formatStack = []
var formatValue = function (value, save) {
  save === true && (window.lastValue = value)
  if (typeof value === "function") { return formatFunc(value) }
  if (!value || typeof value !== "object") {
    return typeof value === "string" ? JSON.stringify(value) : value + "" }
  if (__formatStack.includes(value)) { return value + "" }
  __formatStack.push(value)
  try {
    msg = (value.__proto__ === Object.prototype ? (formatAnyObject(value))
          : !{}.toString.call(value).includes("Array]") ? ""
          : {}.toString.call(value).slice(8, -1)
            + "[ " + ([].map.call(value, formatValue)).join(", ") + " ]"
        ) || ""
  } catch (ex) { var msg = value + "" }
  __formatStack.length--
  return msg
}
var formatError = function (e) {
  if (!(e instanceof Error)) { return "throw " + formatValue(e, true) }
  console.log(e.stack)
  return ("\n" + e.stack).replace(/\n/g, "\n    ")
}
var println = function (msg) {
  var diff = msg === !!msg ? !!msg : false
  msg = "\n" + [].slice.call(arguments, msg === !!msg ? 1 : 0).join(" ")
  if (diff) {
    var span = document.createElement("span")
    span.textContent = msg
    span.style.color = "#c00"
    resultDiv.appendChild(span)
  } else {
    resultDiv.appendChild(new Text(msg))
  }
}
var test = function (code) {
  var result = jsEval(code), value = result[1], error = result[2]
  code = result[0]
  println(!!error, 'eval(' + formatCode(code) + ") => "
      + (error != null ? formatError(error) : formatValue(value, true)))
}
var assert = function (code) {
  var result = jsEval(code), value = result[1], error = result[2]
  code = result[0]
  if (error != null) {
    println(true, 'assert(' + formatCode(code) + ") => " + formatError(error))
    return
  }
  try {
    if (/^[(!+-]/.test(code)) { var value2 = eval(code) }
    else if (/^(const|let|var|for|if|while|return)/.test(code)
        || code.includes("\n")) {
      value2 = new Function(code)()
    } else try {
      value2 = eval(code)
    } catch (e) {
      if (e instanceof SyntaxError) { value2 = new Function(code)() }
      else { throw e }
    }
  } catch (e) {
    println(true, 'Built-ins.eval(' + formatCode(code) + ") => " + formatError(e))
    return
  }
  const isObj = typeof value2 === "object" || typeof value2 === "function"
  value2 = formatValue(value2)
  value = formatValue(value, true)
  var diff = isObj ? value2.replace(/\n\s*/g, " ").replace(/\s{2,}/g, " ")
        !== value.replace(/\n\s*/g, " ").replace(/\s{2,}/g, " ") : value2 !== value
  var msg = !diff ? "(same) " + value
      : "(diff)\n    {{ " + value + " }} !== {{ (std) " + value2 + " }}"
  println(diff, 'assert(' + formatCode(code) + ") => " + msg)
}
//#endregion
function onLoad() {
  resultDiv.innerText = "(Inited)";
  VApi.v.noNative();

  // =============================== assert ===============================
  assert(function () { return {...null, ...123} })
  assert(function () {
    return navigator.userAgent.match(/\bChrom(?:e|ium)\/(\d+)/)
  })
  assert(function () {
    return `; hello ${"world, " + JSON.stringify({secret: [42]})} nihao{2}$${ ` 123` }`
  })
  assert(function () {
    var a = 0, i = 3
    for (let i = 0; i < 13; i += 4) { a += i / 2 }
    for (var i = 0; i < 13; i += 4) { a += i }
    for (i = 0; i < 13;) { a += i * 3; i += 4 }
    for (; i < 20;) { a += i * 3; i += 4 }
    return [a, i]
  })
  assert('{}+[]')
  assert('[]+{}')
  assert('({}+[])')
  assert('[1n, 0b01, 0x1a]')
  assert(function (){
    "use strict";
    return (function(){
      let so='out', identity = (i)=>(console.log(i),i),
      b=function innerName(a=so,s=identity(1),d1=arguments,d2=this,f=s){
          var d1; return [a,s,[].slice.call(d1),d2,f]};
      return b.call({this: "out"}, 'd1-val')
    })()
  })
  assert(function() {
    let a = 0
    do a++
    while (a < 5)
    return a
  })
  assert(function() {
    if (1)
      while (Math.random() > 0.5)
        for (const i of [1 ,2, 3])
          try { 1 } catch { console.log('test: catch') } finally {}
    else if (1)
      if (0)
        for (const i in { 1: 1, 2: 2 })
          do 0
          while (0);
      else if (1)
        while (0)
          if (1) 1; else 0
    else if (0)
      do { a = 1; break }
      while ('')
    else if (2)
      while (1)
        if (1)
          break
    else
      console.log("else")
    let a = 1;
    return { a, b: a }
  })
  assert(function() {
    let 测试a = [0, 1,2,3], a = 测试\u0061
    for(const i测试 of 测试a) {
      if (a.length >= 5) { continue; let s = 1; }
      (i测试 & 1) && (a.push(a.length))
    }
    // for(a in a) { console.log(a) }
    return \u{061}
  })
  assert(function() { const a = 1; return; a = 2 })
  assert(function() {
    let a = {b:{0:{d:{1:{f:"g"}}}}};
    // return a?.b[0]?.["d"][1].f
    return a?.[0]?.().a[1].b[2]()()()
  })
  assert(function() {
    let a = 0, b = 0, c = 0
    foo: { a = 1; break foo; a = 2 }
    loop0: loop1: for (let i = 0; i < 4; i++) { loop2: for (let j = 0; j < 3; j++) {
      if (i === 1 && j === 1) { continue loop0; }
      if (i === 3 && j === 1) break loop2; if (i === 4 && j === 2) break loop1; b++
    } b++; }
    loop0: {
      c++
      loop1: try {
        c++; break loop1
        c++; return c;
      } finally {
        c += 10;
        break loop0;
        c += 10
      }
      c += 100
    }
    c += 1000
    return [a, b, c]
  })
assert(function() {
  const a=(function () {
    "use strict";
const s = { get q () {
  console.log('a');
  Object.defineProperty(s, Symbol.hasInstance, { enumerable: true, configurable: true, get () {
    console.log('return hasInst');
    return "hasInst";
  } }); // throw new Error(123);
  return 's.q';
}, get [(1, Symbol.hasInstance)]() {
  console.log('get sym');
  return 's[hasInst]';
} };
    Object.defineProperty(s, Symbol.hasInstance, {
      enumerable: true, configurable: true,
      get () { console.log('get sym, not enum'); return 's[get sym2]' }
    })
    return s;
  })()
  const c = a.b || a
  // c()
  var get = () => a;
  console.log('spread demo:', {... a})
  var s1 = (b, ... a) => a
  var s2 = s1(... [... [1, 2, 3]])
  return get({a: 12})
})
assert(function () {
  const get2 = 2
  return { get2, has () {
  return 123;
} } })
  assert('(()=>{"use strict"; let a=()=>1+2;return a()})()')
  test("")
  assert('1 + (2 * 3) ** 2 % (4 - 5 | 4 + 1 ? (1.3 ? 2.3 : -3) : 0 ? 1 ? 2 : 3 : 1 ? +1.5 / -+-(+-0.7) : 0)')
  assert('(location.href + "123")["12"] + "\\u{100aa}\\\n888"')
  assert('let a=function(){return function(){return{a:2}}};return[new new a()()?.a, new a()?.()]')
  test('try {return 1+2} catch {} finally {return 4;}')
  assert('({1:2, 3:4, 5:6})')
  test(function () {
    const a = document.createElement("div", "http://www.w3.org/1999/xhtml");
    a.innerText='5',a.innerText *= (navigator.userAgent.match(/\bChrom(?:e|ium)\/(\d+)/) || [0, 90])[1];
    document.documentElement.appendChild(a);
    document.documentElement.appendChild(new Text("test\\\\" + "x25"));
  })
  assert(function () {
    let a
    a={get a(){console.log('get a.a'); return { toString(){ console.log('valueof a.a'); return 'Val(a.a)'} };
        ()=>{console.log('call a.a')}}, set a(v){console.log('set a.a')}}
    b={get b(){console.log('get b.b'); return 2}, set b(v){console.log('set b.b')}}
    var b
    var c={get c () { console.log('get c.c'); return 3; }, set c(v){console.log('set c.c')}}
    let d={get d(){console.log('get d.d'); return c}, set d(v){console.log('set d.d')}}
    const c2={get c(){console.log('get c2.c'); return { toString(){ console.log('valueof c2.c'); return 'c'}}},
        set c(v){console.log('set c.c')}}
    const console = { log (val) { arr.push(val) } }, arr = []
    arr.unshift(d.d[c2.c] += a.a)
    console.log(void a.a)
    arr.push(Object.getOwnPropertyDescriptor(c, "c").get)
    return arr
  })
  assert(function() {
    return (function() {
      let a = this.a
      return a + eval("{ (()=>{return this.b})() }")
    }).call({a:1, b: "this"})
  })
  test(`
    const a = 1
    if a: ab: return 12
    return { 0: 1, 2: 3 }
  `)
  assert(function() {
    let a=[];for(let i = 0; i < 3;i++) {a.push(()=>i)}
    return [a[0](), a[1](), a[2](), void a.length, void a]
  })
  assert(function() {
    function test2(){throw 1}
    function test(){}
    (1,2)
    let a = function(){}
    (1)
    let b = () => { }
    (1,2)
    let c = (d) => { }
    { a = 2 }
    return [a, b, c]
  })
}
    </script>
  </body>
</html>